package parsing;

import java.util.HashMap;
import java.util.Map;

import model.Balise;
import model.Document;
import model.Mot;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import utils.StopList;

/**
 * Class that handles the parsing of XML files for indexing elements
 */
public class DocHandler extends DefaultHandler
{
  /** The stop list. */
  private static final StopList stopList = new StopList();
  
  /** The balise. */
  private Balise balise;
  
  /** The document. */
  private Document document;
  
  /** The chemin. */
  private String chemin = "";
  
  /** The buffer. */
  private StringBuffer buffer;
  
  private Map<String, Map<String, Integer>> balisesInDoc;

  /**
   * Instantiates a new doc handler.
   *
   * @param document the current {@link Document} to be parsed
   */
  public DocHandler(Document document)
  {
    super();
    this.document = document;
  }

  /**
   * Évènement lancé au début du document.
   *
   * @throws SAXException the sAX exception
   */
  public void startDocument() throws SAXException
  {
    balisesInDoc = new HashMap<>();
  }
  
  /**
   * Évènement lancé lorsqu'une balise est rencontrée. Enregistre la balise dans
   * le modèle associé, met à jour le chemin pour les balises enfants et ajoute
   * la balise au document.
   */
  public void startElement(String uri, String localName, String qName, Attributes attributes)
      throws SAXException
  {
    String baliseName = qName.toLowerCase();
    
    chemin += "/";
    
    // Retrieve the id for the balise in the parent
    if(!balisesInDoc.containsKey(chemin))
    {
      balisesInDoc.put(chemin, new HashMap<String, Integer>());
    }
    
    Map<String, Integer> numberOfBalises = balisesInDoc.get(chemin);
    if(!numberOfBalises.containsKey(baliseName))
    {
      numberOfBalises.put(baliseName, Integer.valueOf(0));
    }

    int baliseNumber = numberOfBalises.get(baliseName).intValue() + 1;
    
    balise = new Balise(baliseName, chemin, document, baliseNumber);
    document.addBalise(balise);
    
    numberOfBalises.put(baliseName, Integer.valueOf(baliseNumber));
    balisesInDoc.put(chemin, numberOfBalises);

    chemin += baliseName + "[" + baliseNumber + "]";

    buffer = new StringBuffer();
  }

  /**
   * Évènement lancé à chaque caractère rencontré entre deux balises. Place le
   * caractère dans un buffer.
   *
   * @param ch The characters.
   * @param start The start position in the character array.
   * @param length The number of characters to use from the character array. 
   * @throws SAXException the sAX exception
   */
  public void characters(char[] ch, int start, int length) throws SAXException
  {
    String lecture = new String(ch, start, length);

    if (buffer != null)
    {
      buffer.append(lecture.toLowerCase());
    }

  }

  /**
   * Évènement lancé lorsqu'une balise fermante est rencontrée. Supprime le nom
   * de la dernière balise dans la chemin, et insère les différents termes dans
   * le modèle de la balise.
   *
   * @param uri the uri
   * @param localName the local name
   * @param qName the q name
   * @throws SAXException the sAX exception
   */
  public void endElement(String uri, String localName, String qName) throws SAXException
  {
    if (buffer != null)
    {
      // Récupère tous les mots d'un paragraphe.
      String paragraphe = buffer.toString();

      // Conserve seulement les caractères unicode (alphanumériques + accents)
      String regex = "[^\\p{L}\\p{Nd}]";
      String[] tabMots = paragraphe.split(regex);

      // Pour chaque mot, s'il n'est pas dans la stop liste on l'ajoute aux mots
      // présents dans la balise.
      for (String word : tabMots)
      {
        word = word.replaceAll("\\s+", ""); // Remove unwanted whitespaces 
        if (!word.isEmpty() && !word.equals(" ") && !stopList.contains(word))
        {
          balise.ajouterMot(new Mot(word));
        }
      }
    }

    chemin = chemin.substring(0, chemin.lastIndexOf("/"));
    
    balise = null;
    buffer = null;
  }

  /**
   * Évènement lancé lorsque la fin du document est rencontrée.
   *
   * @throws SAXException the sAX exception
   */
  public void endDocument() throws SAXException
  {
    System.out.println(document.getName() + " parsed.");
    balisesInDoc = null;
    balise = null;
    buffer = null;
    chemin = null;
  }

}
